#include "basic.h"
#include "io.h"
#include "math.h"
#include "connections.h"
#include "List/simclist_impl.h"
#include "boot_methods.h"
#include "file_management.h"
#include "query_management.h"
#include "host_management.h"
#include <pthread.h>

pthread_mutex_t query_myMutex;
int query_mutexState = 0;

void query_initializeMutex() {
	if (query_mutexState == 0) {
		pthread_mutex_init(&query_myMutex, NULL);
		query_mutexState = 1;
	}
}

void send_list(char *name, int socket) {

	int file_list = open(name, O_RDWR, 0666);
	int n;
	size_t lunghezza = lseek(file_list, 0, SEEK_END);
	printf("lunghezza: %zu\n", lunghezza);
	lseek(file_list, 0, SEEK_SET);
	if ((write(socket, &lunghezza, sizeof(size_t))) < 0) { // scrivo la lunghezza del lista
		printf("errore in write su send_list\n");
		return;
	}
	void *mem = malloc(1000);
	while ((n = read(file_list, mem, 1000)) > 0) {
		if ((writen(socket, mem, n)) < 0) { // invio a blocchi di 1000 byte
			printf("errore in write su socket send_list\n");
			return;
		}
		printf("INVIATI: %d BYTE\n", n);
		memset(mem, 0, 1000);
	}
	printf("%s\n", "INVIATA LISTA");
	close(file_list);
	free(mem);
}

void serve_query_request(void *arguments) { // funzione eseguita dai superpeer eletti come "aiutanti" UDP

	check_sublist_struct_t *arg = arguments;

	size_t lunghezza_lista;
	size_t len;

	list_t superpeer_list;
	list_init(&superpeer_list); // inizializzazione
	list_attributes_comparator(&superpeer_list, ip_comparator);
	list_attributes_seeker(&superpeer_list, superpeer_ip_seeker);
	list_attributes_serializer(&superpeer_list,
			element_serializer_superpeer_list);
	list_attributes_copy(&superpeer_list, list_superpeer_meter, 1);

	char nome[40];

	if (read(arg->upperSocket, &nome, 38) < 0) { // leggo nome file
		printf("ERRORE in read su serve_query_request");
		return;
	}

	printf("serve_query_request cerco: %s\n", nome);

	if (read(arg->upperSocket, &lunghezza_lista, sizeof(size_t)) < 0) { // leggo la lista
		printf("ERRORE in read su serve_query_request");
		return;
	}

	printf("LISTA = %zu BYTE\n", lunghezza_lista);

	if (lunghezza_lista == -1)
		return;

	int n;
	void *mem = malloc(1000);
	int file_list = open("QUERY_PEER_SUBLIST", O_RDWR | O_CREAT, 0666);
	while (lunghezza_lista > 0 && ((n = read(arg->upperSocket, mem, 1000)) > 0)) {
		if ((write(file_list, mem, n)) < 0) { // invio a blocchi di 1000 byte
			perror("errore in write su socket in serve_query_request");
			return;
		}
		printf("RICEVUTI %d BYTE\n", n);
		memset(mem, 0, 1000);
		lunghezza_lista -= n;
	}
	if (lunghezza_lista != 0) {
		printf("Lista non ricevuta correttamente\n");
		return;
	}
	close(file_list);
	free(mem);

	if (list_restore_file(&superpeer_list, "QUERY_PEER_SUBLIST", &len) < 0) {
		printf("errore in restore QUERY_PEER_SUBLIST\n");
		return;
	}

	char *elem = list_seek(arg->my_file_list, nome); // controllo prima se ho il file sulla mia lista

	if (elem != NULL) { // se ho in locale la lista dei superpeer con la lista
		printf("file trovato nella lista locale\n");

		list_t fake_peer_list; // lista dei peer associati al superpeer
		list_init(&fake_peer_list);
		list_attributes_comparator(&fake_peer_list, list_comparator_uint32_t);
		list_attributes_seeker(&fake_peer_list, ip_file_seeker);
		list_attributes_copy(&fake_peer_list, list_meter_uint32_t, 1);
		list_attributes_serializer(&fake_peer_list,
				element_serializer_peer_list);
		add_peer(&fake_peer_list, arg->superpeer_ip);

		host_mgt_struct_t arguments;
		arguments.peer_list = &fake_peer_list;
		arguments.socket = arg->upperSocket;

		send_peer_list(&arguments);
		return;
	}

	elem_file_t *elem2 = list_seek(arg->file_list, nome);
	if (elem2 != NULL) {
		printf("file trovato nella lista dei miei peer\n");

		host_mgt_struct_t arguments;
		arguments.peer_list = elem2->lista_peer;
		arguments.socket = arg->peerSocket;

		send_peer_list(&arguments);

		return;
	}

	printf("size della lista: %d \n", list_size(&superpeer_list));

	if (list_size(&superpeer_list) > 0) {
		check_sublist_struct_t arg2;
		arg2.upperSocket = arg->upperSocket;
		arg2.superpeer_list = &superpeer_list;
		arg2.fileName = nome;
		check_request_on_sublist(&arg2);
	}
	return;
}

/*FUNZIONE CHE INTERROGA TUTTI I SUPERPEER DELLA SOTTOLISTA ALLA RICERCA DI UN FILE E RICHIEDE LA LISTA DI PEER CON QUEL FILE AL SP CHE HA RISPOSTO DI AVERLO*/
int check_request_on_sublist(void* arguments) {

	check_sublist_struct_t *arg = arguments;
	list_t *superpeer_list = arg->superpeer_list;
	char* fileName = arg->fileName;

	list_t lista;
	list_init(&lista);
	list_attributes_serializer(&lista, element_serializer_superpeer_list);
	list_attributes_seeker(&lista, superpeer_ip_seeker);
	list_attributes_comparator(&lista, ip_comparator);
	list_attributes_copy(&lista, list_superpeer_meter, 1);
	list_peer_copy(superpeer_list, &lista);

	list_t lista_res;
	list_init(&lista_res);
	list_attributes_serializer(&lista_res, element_serializer_superpeer_list);
	list_attributes_seeker(&lista_res, superpeer_ip_seeker);
	list_attributes_comparator(&lista_res, ip_comparator);
	list_attributes_copy(&lista_res, list_superpeer_meter, 1);
	list_clear(&lista_res);

	printf(arg->fileName);

	list_iterator_start(superpeer_list);
	while (list_iterator_hasnext(superpeer_list)) {
		elem_superpeer_t *curr_sp = ((elem_superpeer_t *) (list_iterator_next(
				superpeer_list)));

		struct in_addr a;
		a.s_addr = curr_sp->ip;
		printf("superpeer: %s\n", inet_ntoa(a));
	}
	list_iterator_stop(superpeer_list);

	peer_initializeMutex();

	if (list_size(superpeer_list) != 0) {

		int s, rcv;
		struct sockaddr_in servaddr, cliaddr_udp;
		size_t len_udp;
		struct timeval timeout; //timeout per la select
		fd_set rset; //insieme di descrittori per la select

		// apro la socket
		int sockfd;
		if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
			printf("errore in socket in check_request_on_sublist\n");
			return 0;
		}

		memset((void *) &servaddr, 0, sizeof(servaddr));
		servaddr.sin_family = AF_INET;
		servaddr.sin_port = htons(CHECK_FILE_PORT);

		memset((void *) &cliaddr_udp, 0, sizeof(cliaddr_udp));
		cliaddr_udp.sin_family = AF_INET;
		cliaddr_udp.sin_port = htons(CHECK_FILE_PORT);

		// ciclo di sendto
		int check_number;

		if (list_size(&lista) > 0) {

			for (check_number = 0; check_number < 2; check_number++) {
				int h = 0;
				list_iterator_start(&lista);
				while (list_iterator_hasnext(&lista)) {

					elem_superpeer_t * corrente =
							(elem_superpeer_t*) list_iterator_next(&lista);

					servaddr.sin_addr.s_addr = corrente->ip;
					char* p = inet_ntoa(servaddr.sin_addr);

					printf("%s\n", fileName);
					if (sendto(sockfd, fileName, strlen(fileName), 0,
							(struct sockaddr *) &servaddr, sizeof(servaddr))
							< 0) {
						printf(
								"errore in sendto su invio nomi richiesta check_request_on_sublist\n"); // non chiudere sgravo
						continue;
					} else
						(printf("inviato check %d a: %s\n", h, p));

					h++;

				}
				list_iterator_stop(&lista);
			}

			/*preparazione alla select*/

			FD_ZERO(&rset);
			FD_SET(sockfd, &rset);

			while (1) {
				timeout.tv_sec = 1;
				timeout.tv_usec = 600;
				s = select(FD_SETSIZE, &rset, NULL, NULL, &timeout);

				if (s == 0) {
					printf("scaduto timeout\n");
					break;
				}

				if (FD_ISSET(sockfd, &rset)) {

					len_udp = sizeof(cliaddr_udp);
					if (recvfrom(sockfd, &rcv, sizeof(int), 0,
							(struct sockaddr *) &cliaddr_udp, &len_udp) < 0) {
						printf("errore in recvfrom check_request_on_sublist\n");
						continue;
					}
					if (rcv == 1) // ha il file
					{
						printf("aggiungo superpeer\n");
						add_superpeer(&lista_res, cliaddr_udp.sin_addr.s_addr); // cliaddr_udp.sin_addr.s_addr
						print_superpeer_list(&lista_res);
					}
					printf("risposta %d ricevuta da %s \n", rcv, inet_ntoa(
							cliaddr_udp.sin_addr));
				}

			}

			if (list_size(&lista_res) > 0) {
				printf("launching getPeerList\n");
				elem_superpeer_t *mySP = (elem_superpeer_t*) list_get_at(
						&lista_res, 0);
				int result = getPeerList(mySP->ip, arg->fileName);
				printf("lanciata get peer list\n");
				if (result == -1)
					return 0;
				printf("lista finale ricevuta\n");
				if (arg->upperSocket == NULL) {
					if (arg->peerSocket != NULL) // sono entrato in seguito a una richiesta del peer
						send_list("QUERY_PEER_LIST", arg->peerSocket); // rimando la lista al peer chiamante
					else {
						return 1; // entrato in seguito ad una mia richiesta (superpeer)
					}

				} // con query serve peer socket
				else {
					send_list("QUERY_PEER_LIST", arg->upperSocket);// mando la lista al superpeer che mi ha eletto
					return 1;
				}

			} else
				printf("lista vuota\n");
		}
	}
	return 0;
}

/*RICHIEDE LA LISTA DI PEER AD UN DETERMINATO SUPERPEER*/
int getPeerList(in_addr_t sp_ip, char* fileName) {

	struct in_addr address;
	address.s_addr = sp_ip;

	int *connection;
	connection = enstablish_tcp_connection(inet_ntoa(address), RCV_LIST_PORT);
	if (connection == -1) {
		printf("Connessione non riuscita\n");
		return -1;
	}

	if (write(*connection, fileName, strlen(fileName)) < 0) {
		fprintf(stderr, "errore in write richiedi SPList");
	}

	printf("RICHIESTA PEER LIST, ATTENDO.\n");

	size_t lunghezza_lista;

	if (read(*connection, &lunghezza_lista, sizeof(size_t)) < 0) {
		printf("ERRORE in read su getPeerList");
		return -1;
	}

	printf("LISTA = %zu BYTE\n", lunghezza_lista);

	if (lunghezza_lista == -1)
		return -1;

	int n;
	void *mem = malloc(1000);
	int file_list = open("QUERY_PEER_LIST", O_RDWR | O_CREAT, 0666);
	while (lunghezza_lista > 0 && ((n = read(*connection, mem, 1000)) > 0)) {
		if ((write(file_list, mem, n)) < 0) { // invio a blocchi di 1000 byte
			perror("errore in write su socket");
			return -1;
		}
		printf("RICEVUTI %d BYTE\n", n);
		memset(mem, 0, 1000);
		lunghezza_lista -= n;
	}
	if (lunghezza_lista != 0) {
		printf("Lista non ricevuto correttamente\n");
		return -1;
	}
	close(file_list);
	free(mem);
	return 0;
}

int query_flooding(void *arguments) {

	check_sublist_struct_t *arg = arguments;
	list_t *superpeer_list = get_superpeer_list(); // prende la lista dal bootstrap
	if (superpeer_list == NULL)
		return 0; // errore nella ricezione lista
	list_attributes_seeker(superpeer_list, superpeer_ip_seeker);
	list_attributes_comparator(superpeer_list, ip_comparator);
	list_attributes_copy(superpeer_list, list_superpeer_meter, 1);

	printf("--------------in query flooding----------\n");

	int superpeer_array[FD_SETSIZE]; // connessioni con i superpeer eletti
	int maxd, maxi, i;

	size_t lunghezza_lista;

	fd_set connessioni; // descrittori pronti
	int ready, temp_socket;
	FD_ZERO(&connessioni);
	/* Inizializza a zero l'insieme dei descrittori */

	for (i = 0; i < FD_SETSIZE; i++) {
		superpeer_array[i] = -1;
	}

	maxd = 0;
	maxi = -1;

	delete_superpeer(superpeer_list, arg->superpeer_ip);

	printf("size lista = %d\n", list_size(superpeer_list));

	int size = 0; // inizio divisione compiti di query

	if ((size = list_size(superpeer_list)) <= 10 /*4*/) {
		if (size == 0) {
			printf("FILE NON TROVATO\n");
			free(superpeer_list);
			return 0;
		}
		printf("la lista ha meno di 4 elementi --> self election \n");

		check_sublist_struct_t arg_self_req;
		arg_self_req.fileName = arg->fileName;
		arg_self_req.upperSocket = NULL; // richiesta sequenziale
		arg_self_req.lowerSocket = arg->lowerSocket;
		arg_self_req.peerSocket = arg->peerSocket;
		arg_self_req.superpeer_list = superpeer_list;

		int success = check_request_on_sublist(&arg_self_req);
		free(superpeer_list);
		return success;
	}

	int rings = (int) sqrt(size);
	int k = 0;

	printf("numere di anelli: %d\n", rings);

	list_t* sub_list[rings];

	for (k = 0; k < rings; k++) {

		list_t new_list;
		list_init(&new_list);
		list_attributes_comparator(&new_list, ip_comparator);
		list_attributes_seeker(&new_list, superpeer_ip_seeker);
		list_attributes_serializer(&new_list, element_serializer_superpeer_list);
		list_attributes_copy(&new_list, list_superpeer_meter, 1);

		list_superpeer_range_copy(superpeer_list, &new_list, k * rings, (k + 1)
				* (rings));
		printf("nuova lista\n");

		sub_list[k] = &new_list;
	}
	int x = 0;
	int finished = 0;
	int iter = 0;

	int calls_done = 0;

	while (!finished && calls_done < rings) {
		int n = pow(2, x); // n = 2 alla x   --> prima interrogazione n=1
		calls_done += n;

		int r = 0;

		while (r < n) { // ciclo di connessioni tcp, (inizialmente una)
			printf("Dentro il while\n");

			printf("Prendo lista in posizione %d\n", (int) ((pow(2, iter) - 1)
					+ r));

			list_t *current_list = sub_list[(int) ((pow(2, iter) - 1) + r)];

			list_attributes_comparator(current_list, ip_comparator);
			list_attributes_seeker(current_list, superpeer_ip_seeker);
			list_attributes_serializer(current_list,
					element_serializer_superpeer_list);
			list_attributes_copy(current_list, list_superpeer_meter, 1);

			printf("Lista corrente\n");

			size_t len;

			int rand_call = (int) ((rand() / RAND_MAX) * (rings - 1)); // es: numero random tra 0 e 2

			printf("Randcall %d\n", rand_call);

			elem_superpeer_t *chosen_superpeer =
					(elem_superpeer_t *) list_get_at(current_list, rand_call);

			struct in_addr call_addr;
			call_addr.s_addr = chosen_superpeer->ip;

			list_delete_at(current_list, rand_call);

			if (list_dump_file(current_list, "TEMP_LIST_QUERY", &len) < 0) {
				printf("Errore in list dump file\n");
				free(superpeer_list);
				return 0;
			}

			printf("size lista da inviare: %d\n", list_size(current_list));

			printf("scelto superpeer: %s\n", inet_ntoa(call_addr));

			int *query_socket = enstablish_tcp_connection(inet_ntoa(call_addr),
					QUERY_HELP_PORT); // contatto il superpeer scelto

			if (query_socket == -1) {
				printf("errore in connessione\n");
				continue;
			}

			write(*query_socket, arg->fileName, 38); // invio nome del file

			send_list("TEMP_LIST_QUERY", *query_socket); // invio lista

			printf("inviato nome: %s %d a %s\n", arg->fileName, strlen(
					arg->fileName), inet_ntoa(call_addr));

			for (i = 0; i < FD_SETSIZE; i++)
				if (superpeer_array[i] < 0) {
					superpeer_array[i] = *query_socket; // array di socket delle connessioni tcp presenti
					break;
				}

			FD_SET(*query_socket, &connessioni);

			if (*query_socket > maxd)
				maxd = *query_socket;
			if (i > maxi)
				maxi = i;

			r++;

		}

		iter++;

		struct timeval timeout = { 3, 300 };

		printf("Attendo le risposte\n");

		if ((ready = select(maxd + 1, &connessioni, NULL, NULL, &timeout)) < 0) { // attendo risposta
			perror("errore in select");
			free(superpeer_list);
			return 0;
		}

		if (ready == 0) {
		}

		for (i = 0; i <= maxi; i++) {
			if ((temp_socket = superpeer_array[i]) < 0)
				continue;

			if (FD_ISSET(temp_socket, &connessioni)) { // se ricevo risposte dai superpeer eletti ricevo la lista

				printf("Risposta ricevuta attendo lista\n");

				if (read(temp_socket, &lunghezza_lista, sizeof(size_t)) < 0) {
					perror("ERRORE");
					continue;
				}

				printf("LISTA = %zu BYTE\n", lunghezza_lista); // ricevo la lista
				int n;
				void *mem = malloc(1000);
				int file_list = open("PEER_ARRIVAL_LIST", O_RDWR | O_CREAT,
						0666);
				while (lunghezza_lista > 0 && ((n
						= read(temp_socket, mem, 1000)) > 0)) {
					if ((write(file_list, mem, n)) < 0) { // invio a blocchi di 1000 byte
						perror("errore in write su socket");
						exit(1);
					}
					printf("RICEVUTI %d BYTE\n", n);
					memset(mem, 0, 1000);
					lunghezza_lista -= n;
				}
				free(mem);
				list_t peer_list; // lista dei peer associati al superpeer
				list_init(&peer_list);
				list_attributes_comparator(&peer_list, list_comparator_uint32_t);
				list_attributes_seeker(&peer_list, ip_file_seeker);
				list_attributes_copy(&peer_list, list_meter_uint32_t, 1);
				list_attributes_serializer(&peer_list,
						element_serializer_peer_list);

				if (list_restore_file(&peer_list, "PEER_ARRIVAL_LIST",
						&lunghezza_lista) < 0) {
					printf("impossibile restorare la lista peer\n");
					free(superpeer_list);
					return 0;
				}

				printf("lista ricevuta\n");

				send_list("PEER_ARRIVAL_LIST", arg->peerSocket);
				free(superpeer_list);
				return 1;

			}

		}

	}
	printf("Esco dal query flooding\n");
}

void get_file_superpeer(void* arguments) {

	check_sublist_struct_t *arg = arguments;
	in_addr_t *addr;
	printf("INSERIRE NOME FILE\n");
	char nome[50];

	while (fgets(nome, 45, stdin) < 0) {
	}

	FD_SET(fileno(stdin), arg->mem_set);

	char file_name2[strlen(nome)];
	strncpy(file_name2, nome, strlen(nome) - 1);
	file_name2[strlen(nome) - 1] = '\0';

	elem_file_t *elem = list_seek(arg->file_list, file_name2);

	if (elem != NULL) { // se ho in locale la lista dei superpeer con la lista
		printf("file trovato nella lista locale\n");
		list_t *peer_list_check = elem->lista_peer;
		addr = list_get_at(peer_list_check, 0);
		struct in_addr address;
		address.s_addr = *addr;

		list_iterator_start(peer_list_check);
		while (list_iterator_hasnext(peer_list_check)) {

			in_addr_t* current = list_iterator_next(peer_list_check);
			struct in_addr address;
			address.s_addr = *current;
			file_arg_t arguments;
			arguments.ip = inet_ntoa(address);
			printf("%s\n", arguments.ip);
			arguments.nome_file = file_name2;
			arguments.port = FILE_PORT;
			if (*current != arg->my_ip) {
				if (request_file(&arguments) < 0)
					continue;
				else
					return;
			}
		}
		list_iterator_stop(peer_list_check);

		return;

	} else { // se non ho la lista in locale
		printf("file non trovato nella lista locale\n");
		check_sublist_struct_t arguments;
		arguments.fileName = file_name2;
		arguments.superpeer_ip = arg->superpeer_ip;
		arguments.upperSocket = NULL;
		arguments.peerSocket = NULL;
		int success = query_flooding(&arguments);
		if (success == 0) {
			printf("FILE NON TROVATO\n");
			return;
		}
		list_t peer_list; // lista dei peer associati al superpeer
		list_init(&peer_list);
		list_attributes_comparator(&peer_list, list_comparator_uint32_t);
		list_attributes_seeker(&peer_list, ip_file_seeker);
		list_attributes_copy(&peer_list, list_meter_uint32_t, 1);
		list_attributes_serializer(&peer_list, element_serializer_peer_list);

		size_t len;
		if (list_restore_file(&peer_list, "QUERY_PEER_LIST", &len) < 0) {
			printf("errore in restore QUERY_PEER_LIST\n");
			return;
		}

		list_iterator_start(&peer_list);
		while (list_iterator_hasnext(&peer_list)) {

			in_addr_t* current = list_iterator_next(&peer_list);
			struct in_addr address;
			address.s_addr = *current;
			file_arg_t arguments;
			arguments.ip = inet_ntoa(address);
			printf("%s\n", arguments.ip);
			arguments.nome_file = file_name2;
			arguments.port = FILE_PORT;
			if (*current != arg->my_ip) {
				if (request_file(&arguments) < 0)
					continue;
				else
					return;
			}
		}
		list_iterator_stop(&peer_list);
		printf("FILE NON TROVATO\n");
	}
	return;

}

int get_file_peer(void* arguments) {

	check_sublist_struct_t *arg = arguments;

	struct in_addr ip;
	ip.s_addr = arg->superpeer_ip;

	printf("superpeer_ip: %s\n", inet_ntoa(ip));

	int s = 0;
	struct timeval timeout; //timeout per la select
	fd_set rset; //insieme di descrittori per la select

	list_t ip_list;
	//	list_attributes_seeker(&ip_list, ip_file_seeker);
	list_init(&ip_list);

	printf("INSERIRE NOME FILE\n");
	fflush(stdout);
	char nome[40];
	while (fgets(nome, 40, stdin) < 0) {
	}

	FD_SET(fileno(stdin), arg->mem_set);

	int *sockfd;

	size_t lunghezza_lista;

	sockfd = enstablish_tcp_connection(inet_ntoa(ip), QUERY_SERVE_PEER_PORT);

	if (sockfd == -1) {
		printf("connessione non stabilita\n");
		return -1;
	}

	printf("INVIO LA RICHIESTA\n");

	if (write(*sockfd, &nome, strlen(nome)) < 0) {
		printf("errore in write su get_file_peer\n");
		return -1;
	}
	printf("RICHIESTO FILE:");
	printf("%s", nome);
	fflush(stdout);

	printf("ATTENDO LA LISTA DI RISPOSTA\n");

	FD_ZERO(&rset);
	FD_SET(*sockfd, &rset);

	timeout.tv_sec = 20; // 20 secondi di timeout per attendere la risposta

	s = select(FD_SETSIZE, &rset, NULL, NULL, &timeout);

	if (s == 0) {
		printf("FILE NON TROVATO, RITENTA!!!\n");
		return -1;
	}

	if (FD_ISSET(*sockfd, &rset)) {

		if (read(*sockfd, &lunghezza_lista, sizeof(size_t)) < 0) {
			printf("errore in read lunghezza lista su get_file peer\n");
			return -1;
		}

		printf("LISTA = %zu BYTE\n", lunghezza_lista);
		int n;
		void *mem = malloc(1000);
		int file_list = open("IP_LIST", O_RDWR | O_CREAT, 0666);
		while (lunghezza_lista > 0 && ((n = read(*sockfd, mem, 1000)) > 0)) {
			if ((write(file_list, mem, n)) < 0) { // invio a blocchi di 1000 byte
				printf("errore in write su socket get_file_peer\n");
				return -1;
			}
			printf("RICEVUTI %d BYTE\n", n);
			memset(mem, 0, 1000);
			lunghezza_lista -= n;
		}
		free(mem);
		size_t len;

		if (list_restore_file(&ip_list, "IP_LIST", &len) < 0) {
			printf("errore list_restore_file su IP_LIST get_file_peer\n");
			return -1;
		}

		printf("%s", "RICEVUTA LISTA E FATTO RESTORE\n");

		if (remove("IP_LIST") < 0) {
			printf("errore in remove file su get_file_peer\n");
			return -1;
		}
		if (close(*sockfd) < 0) {
			printf("errore in close su get_file_peer\n");
			return -1;
		}

	}

	printf("%d\n", list_size(&ip_list));

	char file_name2[strlen(nome)];
	strncpy(file_name2, nome, strlen(nome) - 1);
	file_name2[strlen(nome) - 1] = '\0';

	list_iterator_start(&ip_list);
	while (list_iterator_hasnext(&ip_list)) {

		in_addr_t* current = list_iterator_next(&ip_list);
		struct in_addr address;
		address.s_addr = *current;
		file_arg_t arguments;
		arguments.ip = inet_ntoa(address);
		printf("%s\n", arguments.ip);
		arguments.nome_file = file_name2;
		arguments.port = FILE_PORT;
		if (*current != arg->my_ip) {
			if (request_file(&arguments) < 0)
				continue;
			else
				return 0;
		}
	}
	list_iterator_stop(&ip_list);
	printf("FILE NON TROVATO\n");
	return 0;
}

/*INVIA LA PEER LIST AL SUPERPEER RICHIEDENTE*/
void send_peer_list(void* arguments) {
	printf("Sending peer list\n");
	host_mgt_struct_t * arg = arguments;
	list_t *peer_list = arg->peer_list;
	size_t len;
	printf("preDump\n");
	print_peer_list(peer_list);
	if (list_dump_file(peer_list, "REQ_PEER_LIST", &len) < 0) {
		printf("Errore in list dump file\n");
		return;
	}
	printf("postDump (len is:%zu)\n", len);
	send_list("REQ_PEER_LIST", arg->socket);
	printf("PEER LIST SENT\n");
}

void query_help_routine(void *arguments) {
	check_sublist_struct_t *arg = arguments;

	size_t lunghezza_lista;
	char file_name[75];
	char *filename2;

	list_t superpeer_list;
	list_init(&superpeer_list); // inizializzazione
	list_attributes_comparator(&superpeer_list, ip_comparator);
	list_attributes_seeker(&superpeer_list, superpeer_ip_seeker);
	list_attributes_serializer(&superpeer_list,
			element_serializer_superpeer_list);
	list_attributes_copy(&superpeer_list, list_superpeer_meter, 1);

	size_t len;

	if (read(arg->upperSocket, file_name, 70) < 0) {
		perror("ERRORE in read su query_help_routine");
	}

	if (read(arg->upperSocket, &lunghezza_lista, sizeof(size_t)) < 0) {
		perror("ERRORE in read su query_help_routine");
	}

	printf("LISTA = %zu BYTE\n", lunghezza_lista);
	int n;
	void *mem = malloc(1000);
	int file_list = open("QUERY_SUBLIST", O_RDWR | O_CREAT, 0666);
	while (lunghezza_lista > 0 && ((n = read(arg->upperSocket, mem, 1000)) > 0)) {
		if ((write(file_list, mem, n)) < 0) { // invio a blocchi di 1000 byte
			perror("errore in write su socket");
			exit(1);
		}
		printf("RICEVUTI %d BYTE\n", n);
		memset(mem, 0, 1000);
		lunghezza_lista -= n;
	}
	free(mem);

	if (list_restore_file(&superpeer_list, "QUERY_SUBLIST", &len) < 0) {
		perror("ERRORE IN list_restore_file su IP_LIST");
		exit(0);
	}

	printf("%s", "RICEVUTA LISTA E FATTO RESTORE\n");

	if (remove("QUERY_SUBLIST") < 0) {
		perror("ERRORE IN remove file");
		exit(0);
	}

	check_sublist_struct_t check_arg;
	check_arg.fileName = filename2;
	check_arg.superpeer_list = &superpeer_list;
	check_arg.upperSocket = arg->upperSocket;

	printf("size lista da interrogare %d \n", list_size(&superpeer_list));

	check_request_on_sublist(&check_arg);

}

void find_file_routine(void *arguments) {
	host_mgt_struct_t *arg = arguments;
	list_t *lista_file = arg->file_list;
	list_t *mia_lista_file = arg->my_file_list;

	int n = 0;
	int answ = 0;
	struct sockaddr_in cliaddr_udp;
	size_t len_udp = sizeof(cliaddr_udp);
	char file_name[50];

	memset((void *) &cliaddr_udp, 0, sizeof(cliaddr_udp));
	cliaddr_udp.sin_family = AF_INET;
	cliaddr_udp.sin_port = htons(CHECK_FILE_PORT);

	while (1) {

		memset((void*) &file_name, 0, sizeof(char) * 50);
		if ((n = recvfrom(arg->socket, file_name, 40, 0,
				(struct sockaddr *) &cliaddr_udp, &len_udp)) < 0) {
			perror("errore in recvfrom 1");
		}

		char *file_name2 = file_name;

		if (list_seek(mia_lista_file, file_name2) != NULL || list_seek(
				lista_file, file_name2) != NULL) {

			answ = 1;
			if (sendto(arg->socket, &answ, sizeof(int), 0,
					(struct sockaddr *) &cliaddr_udp, sizeof(cliaddr_udp)) < 0) {
				perror("errore in sendto 1");
			}
		}

		else {

			answ = 0;
			if (sendto(arg->socket, &answ, sizeof(int), 0,
					(struct sockaddr *) &cliaddr_udp, sizeof(cliaddr_udp)) < 0) {
				perror("errore in sendto 2");

			}

		}

	}
}

